#include "MetadataOperacion.h"

#include <iostream>

#define OPERACION_CABECERA "[Operacion]"
#define METADATACLASE_CABECERA "[MetadataClase]"
#define CONDICIONES_CABECERA "[Condiciones]"
#define CONDICION_CABECERA "[Condicion]"
#define DATOS_CABECERA "[Datos]"
#define DATO_CABECERA "[Dato]"
#define CAMPOSSELECCION_CABECERA "[CamposSeleccion]"

#define BORRADO_OPERACION "Borrado"
#define SELECCION_OPERACION "Seleccion"
#define ALTACLASE_OPERACION "AltaClase"
#define INSERCION_OPERACION "Insercion"
#define ACTUALIZACION_OPERACION "Actualizacion"
#define EXISTECLASE_OPERACION "ExisteClase"
#define GETMETADATACLASE_OPERACION "GetMetadataClase"

namespace CapaIndices
{

		MetadataOperacion::MetadataOperacion(){}

		void MetadataOperacion::setOperacion(OpTipo Tipo){
			_TipoOperacion=Tipo;
		}

		void MetadataOperacion::setClase(string Clase){
			_Clase=Clase;
		}

		void MetadataOperacion::setTipoCondicion(OpTipoCondicion TipoCondicion){
			_TipoCondicion=TipoCondicion;
		}

		void MetadataOperacion::addCondicion(const CondicionOperacion& Cond){
			_Condiciones.push_back(Cond);
		}

		void MetadataOperacion::addDato(const ClaveValor& Dat){
			_Datos.push_back(Dat);
		}

		void MetadataOperacion::addColumnaSeleccion(const string& str){
			_ColumnasSeleccion.push_back(str);
		}

		void MetadataOperacion::addLineaMetadataClase(const string& str){
			_MetadataClaseDeshidratada.push_back(str);
		}

		OpTipo MetadataOperacion::getOperacion(){
			return _TipoOperacion;
		}

		string MetadataOperacion::getClase(){
			return _Clase;
		}

		OpTipoCondicion MetadataOperacion::getTipoCondicion(){
			return _TipoCondicion;
		}

		list<CondicionOperacion> MetadataOperacion::getCondiciones(){
			return _Condiciones;
		}

		list<ClaveValor>& MetadataOperacion::getDatos(){
			return _Datos;
		}

		list<string>* MetadataOperacion::getColumnasSeleccion(){
			return &_ColumnasSeleccion;
		}

		list<string>* MetadataOperacion::getMetadataClaseStream(){
			return &_MetadataClaseDeshidratada;
		}

		MetadataOperacion::~MetadataOperacion(){
			_Condiciones.clear();
			_Datos.clear();
			_ColumnasSeleccion.clear();
			_MetadataClaseDeshidratada.clear();
		}

		MetadataOperacion* MetadataOperacion::hidratar(fstream& StreamContacto){
			MetadataOperacion* MO=new MetadataOperacion();

			string str;
			string valores[3];
			getline(StreamContacto,str); //leo titular

			if (str==OPERACION_CABECERA){
				getline(StreamContacto,str); //leo operacion
				MO->setOperacion(hidratarTipoOperacion(str)); //seteo la operacion
				getline(StreamContacto,str); //leo clase
				MO->setClase(hidratarNombreClase(str));  //seteo la clase
				getline(StreamContacto,str); // espacio en blanco
				getline(StreamContacto,str); //leo titular
			}else{
				cerr << "CapaIndices->MetadataOperacion.cpp->MetadataOperacion::hidratar(fstream& StreamContacto)" << endl << "Archivo no valido." << endl;
				throw new exception();
			}

			if (str==METADATACLASE_CABECERA){
				getline(StreamContacto,str); //leo campos
				while (!StreamContacto.eof()){

					MO->addLineaMetadataClase(str);
					getline(StreamContacto,str); //leo siguiente
				}
				//getline(StreamContacto,str); //leo titular
			}else if (MO->getOperacion()==iExisteClase){
				// no necesito mas informacion
			}else if (MO->getOperacion()==iGetMetadataClase){
				// no necesito mas informacion
			}else{
				if (str==CONDICIONES_CABECERA){
					getline(StreamContacto,str); //leo condicion
					MO->setTipoCondicion(hidratarTipoCondicion(str)); //seteo el tipo de condicion
					getline(StreamContacto,str); //leo titular

					while (str==CONDICION_CABECERA){
						for (int i=0;i<3;++i){
							getline(StreamContacto,str); //leo el atributo
							valores[i] = str;
							}
						MO->addCondicion(hidratarCondicion(valores)); //agrego la condicion
						getline(StreamContacto,str);
					}

					getline(StreamContacto,str); //leo el titular
				}

				if (str==DATOS_CABECERA){
					getline(StreamContacto,str); //leo el titular
					while (str==DATO_CABECERA){
						for (int i=0;i<2;++i){
							getline(StreamContacto,str); //leo el atributo
							valores[i] = str;
							}
						MO->addDato(hidratarDatos(valores)); //agrego los datos
						getline(StreamContacto,str);
					}
					getline(StreamContacto,str); //leo el titular
				}

				if (str==CAMPOSSELECCION_CABECERA){
					getline(StreamContacto,str); //leo campos
					while ((str.length()>0)&&(!StreamContacto.eof())){
						MO->addColumnaSeleccion(str.substr(11)); //agrego los campos
						getline(StreamContacto,str);
					}

					if (str != "")
					{
						MO->addColumnaSeleccion(str.substr(11)); //agrego los campos
						getline(StreamContacto,str);
					}
				}
			}

			return MO;
		}

		Operacion* MetadataOperacion::generarOperacion(){
			CondicionCompuesta* CC=NULL;
			MetadataClase* MetadataRecuperada;
			switch (getOperacion()){
				case iAltaClase:
					GuardarMetadata();
					return new OperacionAltaClase(MetadataClase::hidratarDesdeLista(*getMetadataClaseStream()));
					break;
				case iBorrado:
					MetadataRecuperada=RecuperarMetadata();
					CC=getCondicionCompuestaFormatoComun();
					return new OperacionBorrado(MetadataRecuperada,CC);
					break;
				case iInsercion:
					MetadataRecuperada=RecuperarMetadata();
					return new OperacionInsercion(MetadataRecuperada,&getDatos());
					break;
				case iActualizacion:
					MetadataRecuperada=RecuperarMetadata();
					CC=getCondicionCompuestaFormatoComun();
					return new OperacionActualizacion(MetadataRecuperada,&getDatos(),CC);
					break;
				case iSeleccion:
					MetadataRecuperada=RecuperarMetadata();
					CC=getCondicionCompuestaFormatoComun();
					return new OperacionSeleccion(MetadataRecuperada,CC,getColumnasSeleccion());
					break;
				case iExisteClase:
					return new OperacionExisteClase(getClase());
					break;
				case iGetMetadataClase:
					return new OperacionGetMetadataClase(getClase());
					break;
				default:
					cerr << "CapaIndices->MetadataOperacion.cpp->MetadataOperacion::generarOperacion()" << endl << "Operacion invalida." << endl;
					throw new exception();
			}

		}

		MetadataClase* MetadataOperacion::RecuperarMetadata(){
			return HidratadorMetadataClase::hidratarDisco(getClase());
		}
		void MetadataOperacion::GuardarMetadata(){
			
			MetadatosArchivo ArchMetad(getClase()+".Metadata");
			MetadatosGrupo GrupMetad;
			MetadatosRegistro RegMetad;
			MetadatosAtributo AtribMetad("Linea",Cadena);
			//Metadatos de Archivo
			ArchMetad.TamanioBloque=4096*3;

			ArchMetad.MetadatosGrupo=&GrupMetad;
			GrupMetad.MetadatosRegistro=&RegMetad;
			RegMetad.MetadatosAtributos.push_back(AtribMetad);

			ArchivoRLV ClaseDisco(ArchMetad);
			//ClaseDisco.Metadatos=&ArchMetad;
			//ClaseDisco.Nombre=getClase()+".Metadata";


			
			Atributo Atrib;//[getMetadataClaseStream().size()];
			Grupo grp;
			ClaseDisco.CrearGrupo(grp);

			int i=0;

			list<string>* Lista=getMetadataClaseStream();
			//Atrib.reserve(Lista.size());
			//reg.reserve(Lista.size());
			for(list<string>::iterator it=Lista->begin();
				it!=Lista->end();++it){
					Registro*  reg = new Registro();//[getMetadataClaseStream().size()];
					//Atrib= new Atributo();
					//reg=new Registro();
					Atrib.Nombre="Linea";
					Atrib.Tipo=Cadena;
					Atrib.Valor=(*it);
					reg->Atributos.push_back(Atrib);
					grp.Registros.push_back(*reg);

			}



			ClaseDisco.GuardarGrupo(grp,true);
			//throw "guardar Metadata no implementado";			
			this->_LaMetadataDeLaClase=MetadataClase::hidratarDesdeLista(*getMetadataClaseStream());
		}


		CondicionCompuesta* MetadataOperacion::getCondicionCompuestaFormatoComun(){
			CondicionCompuesta* CC;
			switch (this->getTipoCondicion()){
				case iAnd:
					CC=new CondicionCompuestaAND();
					break;
				case iOr:
					CC=new CondicionCompuestaOR();
					break;
			}
			list<CondicionOperacion> listado=getCondiciones();
			for(list<CondicionOperacion>::iterator it=listado.begin();it!=listado.end();it++){
				CC->Subcondiciones.push_back(getCondicionFormatoComun(*it));
			}
			return CC;
		}

		Condicion* MetadataOperacion::getCondicionFormatoComun(CondicionOperacion& CO){
			Condicion* C=getCondicionTipo(CO.getOperador());
			C->ValorCondicion.Nombre=CO.getAtributo();
			C->ValorCondicion.Tipo=getTipoAtributo(CO.getAtributo());
			C->ValorCondicion.Valor=CO.getValor();

			return C;
		}

		TipoRegistro MetadataOperacion::getTipoAtributo(string Atrib){
			MetadataClase* MC=RecuperarMetadata();
			return MC->getTipoAtributo(Atrib);
		}

		Condicion* MetadataOperacion::getCondicionTipo(TipoSimbolo OP){
			switch (OP){
				case Igual:
					return new CondicionIgual();
				case Mayor:
					return new CondicionMayor();
				case MayorIgual:
					return new CondicionMayorIgual();
				case Menor:
					return new CondicionMenor();
				case MenorIgual:
					return new CondicionMenorIgual();
				default:
					cerr << "CapaIndices->MetadataOperacion.cpp->MetadataOperacion::getCondicionTipo(TipoSimbolo OP)" << endl << "Tipo Invalido de Operador" << endl;
					throw new exception();
			}

		}

		OpTipo MetadataOperacion::hidratarTipoOperacion(string str){
			str=str.substr(9);
			if (str==BORRADO_OPERACION){
				return iBorrado;
			}else if (str==SELECCION_OPERACION){
				return iSeleccion;
			}else if (str==ALTACLASE_OPERACION){
				return iAltaClase;
			}else if (str==INSERCION_OPERACION){
				return iInsercion;
			}else if (str==ACTUALIZACION_OPERACION){
				return iActualizacion;
			}else if (str==EXISTECLASE_OPERACION){
				return iExisteClase;
			}else if (str==GETMETADATACLASE_OPERACION){
				return iGetMetadataClase;
			}else
				cerr  << "CapaIndices->MetadataOperacion.cpp->MetadataOperacion::hidratarTipoOperacion(string str)" << endl << "Operacion invalida." << endl;
				throw new exception();
		}

		CondicionOperacion MetadataOperacion::hidratarCondicion(string val[]){
			CondicionOperacion c;
			c.setAtributo(val[0].substr(11));
			c.setOperador(hidratarOperador(val[1]));
			c.setValor(val[2].substr(8));
			return c;
		}

		ClaveValor MetadataOperacion::hidratarDatos(string val[]){
			ClaveValor cv;
			cv.Clave=val[0].substr(11);
			cv.Valor=val[1].substr(8);
			return cv;
		}

		string MetadataOperacion::hidratarNombreClase(string str){
			return str.substr(8);
		}

		TipoSimbolo MetadataOperacion::hidratarOperador(string str){
			str=str.substr(11);
			if (str=="="){
				return Igual;
			}else if (str=="<"){
				return Menor;
			}else if (str=="<="){
				return MenorIgual;
			}else if (str==">"){
				return Mayor;
			}else if (str==">="){
				return MayorIgual;
			}else{
				cerr << "CapaIndices->MetadataOperacion.cpp->MetadataOperacion::hidratarOperador(string str)" << endl << "TipoCond invalida." << endl;
				throw new exception();
			}
		}

		OpTipoCondicion MetadataOperacion::hidratarTipoCondicion(string str){
			str=str.substr(7);
			if (str=="AND"){
				return iAnd;
			}else if (str=="OR"){
				return iOr;
			}else{
				cerr << "CapaIndices->MetadataOperacion.cpp->MetadataOperacion::hidratarTipoCondicion(string str)" << endl << "TipoCond invalida." << endl;
				throw new exception();
			}
		}

}
